package com.hhkj.study.api.user.sevice.biz.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hhkj.study.admin.course.entity.CourseAssocResourceStat;
import com.hhkj.study.admin.course.entity.CourseResourceInfo;
import com.hhkj.study.admin.course.entity.CourseStructure;
import com.hhkj.study.admin.course.enums.CourseAssocResourceTypeEnum;
import com.hhkj.study.admin.course.model.podo.CoursePo;
import com.hhkj.study.admin.course.model.res.UserCourseRes;
import com.hhkj.study.admin.course.service.biz.impl.CourseBizServiceImpl;
import com.hhkj.study.admin.course.service.extend.CourseExtendService;
import com.hhkj.study.admin.dict.service.extend.DictMultiLevelExtendService;
import com.hhkj.study.admin.goods.entity.Goods;
import com.hhkj.study.admin.goods.service.base.GoodsService;
import com.hhkj.study.admin.order.entity.OrderItem;
import com.hhkj.study.admin.order.enums.OrderStatusEnum;
import com.hhkj.study.admin.order.model.podo.OrderItemPo;
import com.hhkj.study.admin.order.service.base.OrderItemService;
import com.hhkj.study.admin.order.service.extend.OrderItemExtendService;
import com.hhkj.study.admin.resource.entity.ResourceCourseBase;
import com.hhkj.study.admin.resource.factory.CourseResourceFactory;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseLivePo;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseOfflinePo;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseVideoPo;
import com.hhkj.study.admin.resource.model.req.ResourceCourseLiveReq;
import com.hhkj.study.admin.resource.model.res.ResourceCourseLiveRes;
import com.hhkj.study.admin.resource.service.biz.ResourceCourseLiveBizService;
import com.hhkj.study.admin.resource.service.extend.ResourceCourseBaseExtendService;
import com.hhkj.study.admin.resource.service.extend.ResourceCourseLiveExtendService;
import com.hhkj.study.admin.resource.service.extend.ResourceCourseOfflineExtendService;
import com.hhkj.study.admin.resource.service.extend.ResourceCourseVideoExtendService;
import com.hhkj.study.admin.user.entity.UserExercise;
import com.hhkj.study.admin.user.entity.UserListen;
import com.hhkj.study.admin.user.model.podo.UserListenDo;
import com.hhkj.study.admin.user.service.base.UserListenService;
import com.hhkj.study.admin.user.service.extend.UserListenExtendService;
import com.hhkj.study.api.user.common.checker.AccountChecker;
import com.hhkj.study.api.user.common.handler.AccountSessionHandler;
import com.hhkj.study.api.user.enums.CourseStructQuerySourceEnum;
import com.hhkj.study.api.user.model.req.*;
import com.hhkj.study.api.user.model.res.*;
import com.hhkj.study.api.user.sevice.biz.ApiCustomerUserLessonBizService;
import com.hhkj.study.api.user.sevice.biz.ApiUserCourseBizService;
import com.hhkj.study.common.cache.GlobalCache;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.cont.SysConst;
import com.ruoyi.common.enums.DictCodeEnum;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @Author luowu
 * @Date 2024/10/10 涵 16:51:16
 */
@Service
public class ApiCustomerUserLessonBizServiceImpl implements ApiCustomerUserLessonBizService {

    @Resource
    private AccountChecker accountChecker;

    @Resource
    private CourseExtendService courseExtendService;

    @Resource
    private ResourceCourseLiveBizService resourceLiveBizService;

    @Resource
    private ResourceCourseVideoExtendService resourceCourseVideoExtendService;

    @Resource
    private DictMultiLevelExtendService dictMultiLevelExtendService;

    @Resource
    private ApiUserCourseBizService apiUserCourseBizService;

    @Resource
    private OrderItemExtendService orderItemExtendService;

    @Resource
    private UserListenExtendService userListenExtendService;

    @Resource
    private UserListenService userListenService;

    @Resource
    private ResourceCourseLiveExtendService liveExtendService;

    @Resource
    private ResourceCourseVideoExtendService videoExtendService;

    @Resource
    private ResourceCourseOfflineExtendService offlineExtendService;

    @Resource
    private OrderItemService orderItemService;

    @Resource
    private GoodsService goodsService;

    @Resource
    private GlobalCache globalCache;


    @Override
    public List<ApiCustomerUserCourseListRes> courseList(ApiCustomerUserCourseListReq req) {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        List<UserCourseRes> userCourseResList = courseExtendService.getCourseListByUserId(currentCustomerUser.getId(), req.getCategoryId());
        // 该参数表示查询过期的课程
        if (SysConst.Flag.IS.getFlag().equals(req.getExpireFlag())) {
            Date currentDate = DateUtils.currentDateYYYYMMDD();
            userCourseResList = userCourseResList.stream().filter(e -> currentDate.after(e.getValidEndTime())).collect(Collectors.toList());
        }
        return BeanUtil.copyToList(userCourseResList, ApiCustomerUserCourseListRes.class);
    }

    @Override
    public ApiCustomerUserCourseRes courseDetail(ApiCustomerUserCourseBaseReq req) {
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        return apiUserCourseBizService.userCourseByCourseId(currentCustomerUser.getId(), req.getCourseId(), req.getOrderItemId());
    }

    @Override
    public List<ApiCourseResourceLiveRes> liveDayList(ApiUserLiveLessonDayReq req) {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        List<UserCourseRes> userCourseList = courseExtendService.getCourseListByUserId(currentCustomerUser.getId(), req.getCourseType());
        List<Long> courseIdList = dictMultiLevelExtendService.getAllCourseIdByParentId(req.getCourseType());

        // 获取课程下关联的资源
        List<CourseAssocResourceStat> courseAssocResourceList = userCourseList.stream()
                .filter(e -> courseIdList.contains(e.getCategoryId()) && ObjectUtil.isNotNull(e.getAssocResourceStat()) && CollectionUtil.isNotEmpty(e.getAssocResourceStat().getCourseAssocResourceList()))
                .flatMap(userCourse -> userCourse.getAssocResourceStat().getCourseAssocResourceList().stream())
                .collect(Collectors.toList());
        // 课程关联的直播id列表
        List<Long> courseResouceLiveIdList = courseAssocResourceList.stream()
                .filter(e -> CourseAssocResourceTypeEnum.LIVE.getCode().equals(e.getResourceType()) && e.getResourceCount() > 0)
                .flatMap(courseResource -> courseResource.getResourceIdList().stream()).collect(Collectors.toList());

        if (CollectionUtil.isEmpty(courseResouceLiveIdList)) {
            return Lists.newArrayList();
        }
        ResourceCourseLiveReq resourceCourseLiveReq = new ResourceCourseLiveReq();
        resourceCourseLiveReq.setIdList(courseResouceLiveIdList);
        List<ResourceCourseLiveRes> resourceCourseLiveResList = resourceLiveBizService.list(resourceCourseLiveReq);
        resourceCourseLiveResList.sort(Comparator.comparing(ResourceCourseLiveRes::getStartTime).reversed());
        return BeanUtil.copyToList(resourceCourseLiveResList, ApiCourseResourceLiveRes.class);
    }

    @Override
    public List<ApiCustomerUserCourseStructureRes> timeTable(ApiCustomerUserCourseGetReq req) {
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        CoursePo coursePo = courseExtendService.getPoByIdNotNull(req.getCourseId());
        List<ApiCustomerUserCourseStructureRes> userCourseStructureList = BeanUtil.copyToList(coursePo.getStructureJson(), ApiCustomerUserCourseStructureRes.class);
        Map<Long, ResourceCourseVideoPo> courseVideoMap = courseVideoMap(req.getCourseId());
        // 获取课程表结构中需要的资源字段
        Triple<Map<Long, ResourceCourseLivePo>, Map<Long, ResourceCourseBase>, Map<Long, ResourceCourseOfflinePo>> docAndLiveResourceMapTripe = doCourseStructResource(coursePo);
        ApiUserCourseStudyRecordRes userCourseStudyRecord = apiUserCourseBizService.getUserCourseStudyRecord(currentCustomerUser.getId(), req.getCourseId(), req.getOrderItemId());
        // 通过查询来源类型隐藏课程表中的资源类型
        List<ApiCustomerUserCourseStructureRes> resultList = filterPredicate(userCourseStructureList, req.getStructId(),  CourseStructQuerySourceEnum.getByCode(req.getSourceType()));
        resultList.parallelStream().forEach(userCourseStruct -> {
            handlerLastChildren(userCourseStruct, userCourseStudyRecord, docAndLiveResourceMapTripe, courseVideoMap);
            // 给每个目录设置课程学习进度
            processCourseCatalogProgress(userCourseStruct);
        });
        return resultList;
    }



    @Override
    public boolean videoProgressSave(ApiCustomerUserListenSaveReq req) {
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());

        // 封装参数 保存用户听课记录如果时长比之前短则不更新,如果用户记录不存在新增一条记录
        UserListenDo userListenDo = new UserListenDo();
        userListenDo.setUserId(currentCustomerUser.getId());
        userListenDo.setCourseId(req.getCourseId());
        userListenDo.setVideoId(req.getVideoId());
        userListenDo.setListenedDuration(req.getVideoDuration());
        userListenDo.setOrderItemId(req.getOrderItemId());
        userListenDo.setStartStudyTime(req.getStartStudyTime());
        userListenDo.setEndStudyTime(req.getEndStudyTime());
        return userListenExtendService.saveListenRecord(userListenDo);
    }

    @Override
    public ApiCustomerUserStudyReportRes studyReport(ApiCustomerUserCourseBaseReq req) {
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        req.setUserId(currentCustomerUser.getId());
        return apiUserCourseBizService.courseStudyReport(req);
    }

    @Override
    public Long videoHistory(ApiCustomerUserVideoHistoryReq req) {
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        List<UserListen> userListenList = userListenExtendService
                .listByUserCourseIdVideoId(currentCustomerUser.getId(), req.getOrderItemId(), req.getCourseId(), req.getVideoId());
        Optional<UserListen> maxUserListenOpt = userListenList.stream()
                .max(Comparator.comparing(UserListen::getEndStudyTime, Comparator.nullsLast(Comparator.naturalOrder())));
        Long defaultListenDuration = 0L;
        if (maxUserListenOpt.isPresent()) {
            UserListen userListen = maxUserListenOpt.get();
            if (userListen.getLastListenedDuration() < userListen.getVideoDuration()) {
                defaultListenDuration = userListen.getLastListenedDuration();
            }
        }
        return defaultListenDuration;
    }

    @Override
    public List<ApiCustomerUserLessonTypeRes> lessonType() {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        // 用户订单中所有的商品所属的课程类型
        LambdaQueryWrapper<OrderItem> orderItemQuery = new LambdaQueryWrapper<>();
        orderItemQuery.select(OrderItem::getGoodsId).eq(OrderItem::getBuyerId, currentCustomerUser.getId())
                .eq(OrderItem::getStatus, OrderStatusEnum.PAID.getCode()).orderByDesc(OrderItem::getCreateTime);
        List<OrderItem> orderItemList = orderItemService.list(orderItemQuery);
        Set<Long> goodsIdSet = orderItemList.stream().map(OrderItem::getGoodsId).collect(Collectors.toSet());
        List<ApiCustomerUserLessonTypeRes> resList = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(goodsIdSet)) {
            LambdaQueryWrapper<Goods> goodsLambdaQuery = new LambdaQueryWrapper<>();
            goodsLambdaQuery.select(Goods::getCategoryId).in(Goods::getId, goodsIdSet);
            Set<Long> userAllCourseTypeSet = goodsService.list(goodsLambdaQuery).stream().map(Goods::getCategoryId).collect(Collectors.toSet());
            Map<Long, String> dictMap = globalCache.cacheSimpleDictMap(Lists.newArrayList(DictCodeEnum.COURSE_TYPE.getCode()));
            userAllCourseTypeSet.forEach(courseType -> {
                ApiCustomerUserLessonTypeRes res = new ApiCustomerUserLessonTypeRes();
                res.setCourseType(courseType);
                res.setCourseTypeName(dictMap.get(courseType));
                resList.add(res);
            });
        }
        return resList;
    }

    @Override
    public ApiCustomerUserListenRecentRes recent() {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        LambdaQueryWrapper<UserListen> userListenQuery = new LambdaQueryWrapper<>();
        userListenQuery.eq(UserListen::getUserId, currentCustomerUser.getId()).isNotNull(UserListen::getStartStudyTime)
                .orderByDesc(UserListen::getEndStudyTime).last("LIMIT 1");
        UserListen userListen = userListenService.getOne(userListenQuery);
        if (ObjectUtil.isNull(userListen)) {
            return null;
        }
        ApiCustomerUserListenRecentRes userListenRecentRes = BeanUtil.copyProperties(userListen, ApiCustomerUserListenRecentRes.class);
        userListenRecentRes.setListenedDuration(userListenRecentRes.getLastListenedDuration());
        ResourceCourseVideoPo courseVideo = resourceCourseVideoExtendService.getPoByIdNotNull(userListen.getVideoId());
        CoursePo coursePo = courseExtendService.getPoByIdNotNull(userListen.getCourseId());
        // 获取到视频所在的目录
        Optional<List<CourseStructure>> structureJsonListOpt = Optional.ofNullable(coursePo.getStructureJson());
        if (structureJsonListOpt.isPresent()) {
            String defaultResourceType;
            Long typeAssocId;
            if (Constants.LIVE_VIDEO_TYPE.equals(courseVideo.getType())) {
                defaultResourceType = CourseAssocResourceTypeEnum.LIVE.getCode();
                ResourceCourseLiveRes liveByVideoId = resourceLiveBizService.getLiveByVideoId(courseVideo.getId());
                typeAssocId = liveByVideoId.getId();
            } else {
                defaultResourceType = CourseAssocResourceTypeEnum.VIDEO.getCode();
                typeAssocId = courseVideo.getId();
            }
            List<CourseStructure> courseStructureList = BeanUtil.copyToList(structureJsonListOpt.get(), CourseStructure.class);
            Optional<CourseStructure> first = courseStructureList.stream()
                    .filter(e -> ObjectUtil.isNotNull(e) && CollectionUtil.isNotEmpty(e.getChildren()))
                    .flatMap(courseResource -> courseResource.getChildren().stream()
                            .filter(e -> ObjectUtil.isNotNull(e)
                                    && typeAssocId.equals(e.getTypeAssoId())
                                    && defaultResourceType.equals(e.getType()))).findFirst();
            first.ifPresent(e -> {
                userListenRecentRes.setStructId(e.getId());
                userListenRecentRes.setStructParentId(e.getParentId());
                userListenRecentRes.setCourseName(e.getName());
            });
        }
        userListenRecentRes.setVideoUrl(courseVideo.getUrl());
        userListenRecentRes.setTeacherPicUrl(courseVideo.getTeacherPicUrl());
        return userListenRecentRes;
    }

    @Override
    public List<ApiCustomerUserCourseStructureRes> structure(ApiCustomerUserCourseGetReq req) {
        List<ApiCustomerUserCourseStructureRes> userCourseStructureList = getCustomerUserCourseStructureRes(req);
        ApiCustomerUserBaseRes currentAccount = AccountSessionHandler.getCurrentAccount();
        ApiUserCourseStudyRecordRes userCourseStudyRecord = apiUserCourseBizService.getUserCourseStudyRecord(currentAccount.getId(), req.getCourseId(), req.getOrderItemId());
        // 给每个目录设置课程学习进度
        userCourseStructureList.forEach(userCourse -> {
            processCourseCatalogProgressNew(userCourse, userCourseStudyRecord);
            userCourse.setChildren(null);
        });
        return userCourseStructureList;
    }


    @Override
    public ApiCustomerUserCourseStructureRes structureDetail(ApiCustomerUserCourseGetReq req) {
        if (ObjectUtil.isNull(req.getStructId())) {
            throw new GlobalException("参数缺失,课程结构id不能为空");
        }
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        CoursePo coursePo = courseExtendService.getPoByIdNotNull(req.getCourseId());
        List<ApiCustomerUserCourseStructureRes> userCourseStructureList =
                BeanUtil.copyToList(coursePo.getStructureJson(), ApiCustomerUserCourseStructureRes.class);
        Map<Long, ResourceCourseVideoPo> courseVideoMap = courseVideoMap(req.getCourseId());
        // 获取课程表结构中需要的资源字段
        Triple<Map<Long, ResourceCourseLivePo>, Map<Long, ResourceCourseBase>, Map<Long, ResourceCourseOfflinePo>> docAndLiveResourceMapTripe = doCourseStructResource(coursePo);
        ApiUserCourseStudyRecordRes userCourseStudyRecord =
                apiUserCourseBizService.getUserCourseStudyRecord(currentCustomerUser.getId()
                        , req.getCourseId(), req.getOrderItemId());
        Optional<ApiCustomerUserCourseStructureRes> structLessonCourseStruct =
                userCourseStructureList.stream().filter(e -> req.getStructId().equals(e.getId())).findFirst();
        if (!structLessonCourseStruct.isPresent()) {
            return new ApiCustomerUserCourseStructureRes();
        }
        // 组装该结构下资源
        handlerLastChildren(structLessonCourseStruct.get(), userCourseStudyRecord, docAndLiveResourceMapTripe, courseVideoMap);
        return structLessonCourseStruct.get();
    }


    /**
     * 用户账号是否正常并且是否该买课程
     * @param courseId
     * @param orderItemId
     */
    private ApiCustomerUserBaseRes checkUserValidHasCourse(Long courseId, Long orderItemId) {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        OrderItemPo orderItemPo = orderItemExtendService.getPoByIdNotNull(orderItemId);
        // 课程未购买 不是该用户订单中的课程 逻辑为 有该订单并且订单状态成功而且购买者是该用户课程也包含传递的课程id
        boolean hasNoUserOrderItemCourse = !(ObjectUtil.isNotNull(orderItemPo)
                && OrderStatusEnum.PAID.getCode().equals(orderItemPo.getStatus())
                && orderItemPo.getBuyerId().equals(currentCustomerUser.getId())
                && orderItemPo.getCourseIdList().contains(courseId));
        if (hasNoUserOrderItemCourse) {
            throw new GlobalException("该用户没有购买这个课程");
        }
        return currentCustomerUser;
    }

    /**
     * 递归课程下资源并且设置课程学习情况
     * @param courseStructure 课程表结构
     * @param userCourseStudyRecord 用户学习记录
     * @return
     */
    private void handlerLastChildren(ApiCustomerUserCourseStructureRes courseStructure
            , ApiUserCourseStudyRecordRes userCourseStudyRecord
            , Triple<Map<Long, ResourceCourseLivePo>
            , Map<Long, ResourceCourseBase>
            , Map<Long, ResourceCourseOfflinePo>> docAndLiveResourceMapTripe
            , Map<Long, ResourceCourseVideoPo> courseVideoMap){
        if (CollectionUtil.isEmpty(courseStructure.getChildren())) {
            return;
        }
        /*
         * 1. 只有视频 直播 线下课类型的资源才会关联题库/资料
         * 2. 直播 视频类型需要从用户听课记录中获取到用户听课时长
         * 3. 课程表中如果有直播类型需要展示直播地址和关联的视频id
         * 4. 课程关联的是资料,需要展示资料url提供下载
         */
        // 之前是递归处理现在改为直接处理,只支持两级操作
        courseStructure.getChildren().parallelStream().forEach(child -> {
            if (CollectionUtil.isEmpty(child.getChildren())) {
                // 课程关联的是视频
                if (CourseAssocResourceTypeEnum.CAN_ASSOC_BANK_TYPE_LIST.contains(child.getType())) {
                    // 视频信息补充
                    Map<Long, UserListen> userListenMap = userCourseStudyRecord.getUserListenMap();
                    if (ObjectUtil.isNotNull(child.getTypeAssoId())) {
                        UserListen userListen = null;
                        if (CourseAssocResourceTypeEnum.VIDEO.getCode().equals(child.getType())) {
                            userListen = userListenMap.getOrDefault(child.getTypeAssoId(), new UserListen());
                            ResourceCourseVideoPo resourceCourseVideo =
                                    courseVideoMap.getOrDefault(child.getTypeAssoId(), new ResourceCourseVideoPo());
                            child.setVideoUrl(resourceCourseVideo.getUrl());
                            child.setVideoId(resourceCourseVideo.getId());
                            child.setVideoDuration(resourceCourseVideo.getDuration());
                        } else if (CourseAssocResourceTypeEnum.LIVE.getCode().equals(child.getType())) {
                            ResourceCourseLivePo resourceCourseLivePo =
                                    docAndLiveResourceMapTripe.getLeft().getOrDefault(child.getTypeAssoId(), new ResourceCourseLivePo());
                            child.setLiveUrl(resourceCourseLivePo.getUrl());
                            if (ObjectUtil.isNotNull(resourceCourseLivePo.getVideoId())) {
                                userListen = userListenMap.get(resourceCourseLivePo.getVideoId());
                                child.setVideoUrl(resourceCourseLivePo.getVideoUrl());
                                child.setVideoId(resourceCourseLivePo.getVideoId());
                                if (ObjectUtil.isNull(userListen)) {
                                    child.setVideoDuration(resourceCourseLivePo.getVideoDruation());
                                }
                            }
                            child.setLessonStartTime(resourceCourseLivePo.getStartTime());
                            child.setLessEndTime(resourceCourseLivePo.getEndTime());
                        } else {
                            ResourceCourseOfflinePo resourceCourseOfflinePo =
                                    docAndLiveResourceMapTripe.getRight().getOrDefault(child.getTypeAssoId(), new ResourceCourseOfflinePo());
                            child.setLessonStartTime(resourceCourseOfflinePo.getStartTime());
                            child.setLessEndTime(resourceCourseOfflinePo.getEndTime());
                        }
                        if (ObjectUtil.isNotNull(userListen) && ObjectUtil.isNotNull(userListen.getId())) {
                            child.setVideoListenStatus(userListen.getListeningStatus());
                            child.setVideoDuration(userListen.getVideoDuration());
                        }
                    }

                    if (ObjectUtil.isNotNull(child.getDocId())) {
                        Map<Long, ResourceCourseBase> resourceCourseMap = docAndLiveResourceMapTripe.getMiddle();
                        ResourceCourseBase resourceCourse = resourceCourseMap.getOrDefault(child.getDocId(), new ResourceCourseBase());
                        child.setDocUrl(resourceCourse.getDocUrl());
                        child.setDocSize(StringUtils.formatSize(resourceCourse.getDocSize()));
                    }
                    // 视频关联的题库
                    Map<Long, UserExercise> userExerciseMap = userCourseStudyRecord.getUserExerciseMap();
                    if (ObjectUtil.isNotNull(child.getQuestionId())) {
                        UserExercise userExercise = userExerciseMap.get(child.getQuestionId());
                        if (ObjectUtil.isNotNull(userExercise)) {
                            child.setQuestionBankCorrectCount(userExercise.getCorrectCount());
                            child.setQuestionBankDoCount(userExercise.getExerciseCount());
                            child.setQuestionBankTotalCount(userExercise.getQuestionCount());
                            child.setQuestionBankStudyStatus(userExercise.getExerciseStatus());
                        }
                    }

                }
                if (CourseAssocResourceTypeEnum.DOC.getCode().equals(child.getType())) {
                    Map<Long, ResourceCourseBase> resourceCourseMap = docAndLiveResourceMapTripe.getMiddle();
                    ResourceCourseBase resourceCourse = resourceCourseMap.getOrDefault(child.getTypeAssoId(), new ResourceCourseBase());
                    child.setDocUrl(resourceCourse.getDocUrl());
                    child.setDocSize(StringUtils.formatSize(resourceCourse.getDocSize()));
                }
            }
        });
    }

    /**
     * 处理课程结构资源，生成直播资源和文档资源的映射
     *
     * @param coursePo 课程信息对象，包含关联资源状态
     * @return 返回一个Pair对象，包含直播资源和文档资源的映射
     */
    private Triple<Map<Long, ResourceCourseLivePo>, Map<Long, ResourceCourseBase>
            , Map<Long, ResourceCourseOfflinePo>> doCourseStructResource(CoursePo coursePo) {

        Optional<CourseResourceInfo> assocResourceStatOptional = Optional.ofNullable(coursePo.getAssocResourceStat());
        // 课程下所有的直播资源
        Optional<CourseAssocResourceStat> courseAssocResourceLiveStat = assocResourceStatOptional
                .map(CourseResourceInfo::getCourseAssocResourceList)
                .orElse(Lists.newArrayList()).stream()
                .filter(e -> CourseAssocResourceTypeEnum.LIVE.getCode().equals(e.getResourceType())).findFirst();
        Map<Long, ResourceCourseLivePo> courseliveMap = Maps.newHashMap();
        if (courseAssocResourceLiveStat.isPresent()) {
            List<Long> resourceIdList = courseAssocResourceLiveStat.get().getResourceIdList();
            List<ResourceCourseLivePo> resourceCourseLivePoList = liveExtendService.listByIdList(resourceIdList);
            if (CollectionUtil.isNotEmpty(resourceCourseLivePoList)) {
                courseliveMap = resourceCourseLivePoList.stream()
                        .collect(Collectors.toMap(ResourceCourseLivePo::getId, Function.identity()));
            }
        }

        // 课程下所有的线下课资源
        Optional<CourseAssocResourceStat> courseAssocResourceOfflineStat = assocResourceStatOptional
                .map(CourseResourceInfo::getCourseAssocResourceList)
                .orElse(Lists.newArrayList()).stream()
                .filter(e -> CourseAssocResourceTypeEnum.OFFLINE.getCode().equals(e.getResourceType())).findFirst();
        Map<Long, ResourceCourseOfflinePo> courseOfflineMap = Maps.newHashMap();
        if (courseAssocResourceOfflineStat.isPresent()) {
            List<Long> resourceIdList = courseAssocResourceOfflineStat.get().getResourceIdList();
            List<ResourceCourseOfflinePo> resourceCourseOfflinePoList = offlineExtendService.listByIdList(resourceIdList);
            if (CollectionUtil.isNotEmpty(resourceCourseOfflinePoList)) {
                courseOfflineMap = resourceCourseOfflinePoList.stream().collect(Collectors.toMap(ResourceCourseOfflinePo::getId, Function.identity()));
            }
        }

        // 优化代码逻辑,抽取相似逻辑 暂时放这里
//        Map<Long, ? extends ResourceCourseBase> resourceListByType = getResourceListByType(assocResourceStatOptional, CourseAssocResourceTypeEnum.LIVE);


        Map<Long, ResourceCourseBase> docIdUrlMap = Maps.newHashMap();
        if (assocResourceStatOptional.isPresent()) {
            Map<String, List<? extends ResourceCourseBase>> resourceGroupTypeMap = CourseBizServiceImpl.getResourceGroupTypeMap(assocResourceStatOptional.get().getCourseAssocResourceList());
            resourceGroupTypeMap.forEach((resourceType, resourceList) -> {
                if (CourseAssocResourceTypeEnum.CAN_ASSOC_BANK_TYPE_LIST.contains(resourceType)) {
                    Map<Long, ResourceCourseBase> docIdPartMap = resourceList.stream().filter(e -> ObjectUtil.isNotNull(e.getDocId())).collect(Collectors.toMap(ResourceCourseBase::getDocId, Function.identity(), (o, n) -> n));
                    docIdUrlMap.putAll(docIdPartMap);
                }
                if (CourseAssocResourceTypeEnum.DOC.getCode().equals(resourceType)) {
                    Map<Long, ResourceCourseBase> docIdPartMap = resourceList.stream().collect(Collectors.toMap(ResourceCourseBase::getId, Function.identity()));
                    docIdUrlMap.putAll(docIdPartMap);
                }
            });
        }
        return Triple.of(courseliveMap, docIdUrlMap, courseOfflineMap);
    }


    /**
     * 递归课程表去除不符合条件的节点
     * @param list
     * @param structId 结构id通过id过滤其他资源
     * @param predicate
     * @return
     */
    private static List<ApiCustomerUserCourseStructureRes> filterPredicate(List<ApiCustomerUserCourseStructureRes> list
            , Long structId, Predicate<ApiCustomerUserCourseStructureRes> predicate) {
        if (CollectionUtil.isEmpty(list)) {
            return Lists.newArrayList();
        }
        List<ApiCustomerUserCourseStructureRes> filteredList = new ArrayList<>();
        for (ApiCustomerUserCourseStructureRes item : list) {
            if (ObjectUtil.isNotNull(structId) && item.getId() != structId) {
                continue;
            }
            List<ApiCustomerUserCourseStructureRes> filteredChildren = filterChildren(item.getChildren(), predicate);
            if (predicate.test(item) || CollectionUtil.isNotEmpty(filteredChildren)) {
                item.setChildren(filteredChildren);
                filteredList.add(item);
            }
        }
        return filteredList;
    }

    private static List<ApiCustomerUserCourseStructureRes> filterChildren(List<ApiCustomerUserCourseStructureRes> list
            , Predicate<ApiCustomerUserCourseStructureRes> predicate) {
        if (CollectionUtil.isEmpty(list)) {
            return list;
        }
        List<ApiCustomerUserCourseStructureRes> filteredList = new ArrayList<>();
        boolean hasFlag = false;
        for (ApiCustomerUserCourseStructureRes item : list) {
            List<ApiCustomerUserCourseStructureRes> filteredChildren = filterChildren(item.getChildren(), predicate);
            if (predicate.test(item) || CollectionUtil.isNotEmpty(filteredChildren)) {
                hasFlag = true;
                item.setChildren(filteredChildren);
                filteredList.add(item);
            }
        }
        return hasFlag ? filteredList : null;
    }

    /**
     * 获取用户课程结构信息
     *
     * 此方法用于根据用户请求获取特定课程的结构信息，首先确保用户账户合法，然后验证用户是否已购买该课程，
     * 最后从课程结构中提取并返回用户课程结构信息
     *
     * @param req 用户请求对象，包含课程ID和订单项ID
     * @return 返回用户课程结构信息列表，如果用户未购买课程或课程结构为空，则返回空列表
     */
    private List<ApiCustomerUserCourseStructureRes> getCustomerUserCourseStructureRes(ApiCustomerUserCourseGetReq req) {
        // 校验账号是否合法
        // 检查用户是否购买课程
        ApiCustomerUserBaseRes currentCustomerUser = checkUserValidHasCourse(req.getCourseId(), req.getOrderItemId());
        CoursePo coursePo = courseExtendService.getPoByIdNotNull(req.getCourseId());
        List<ApiCustomerUserCourseStructureRes> userCourseStructureList = BeanUtil.copyToList(coursePo.getStructureJson()
                , ApiCustomerUserCourseStructureRes.class);
        if (CollectionUtil.isEmpty(userCourseStructureList)) {
            return Lists.newArrayList();
        }
        return userCourseStructureList;
    }

    /**
     * 课程中所有的视频资源
     * @param courseId
     * @return
     */
    private Map<Long, ResourceCourseVideoPo> courseVideoMap(Long courseId) {
        CourseAssocResourceStat courseAssocResourceStat =
                courseExtendService.getCourseAssocResourceStat(courseId, CourseAssocResourceTypeEnum.VIDEO);
        Map<Long, ResourceCourseVideoPo> courseVideoMap = Maps.newHashMap();
        if (ObjectUtil.isNotNull(courseAssocResourceStat)) {
            if (CollectionUtil.isNotEmpty(courseAssocResourceStat.getResourceIdList())) {
                List<ResourceCourseVideoPo> courseVideoList =
                        videoExtendService.listByIdList(courseAssocResourceStat.getResourceIdList());
                courseVideoMap = courseVideoList.stream()
                        .collect(Collectors.toMap(ResourceCourseVideoPo::getId, Function.identity()));
            }

        }
        return courseVideoMap;
    }

    /**
     * 处理课程目录的进度
     * 该方法用于计算用户在课程结构中的学习进度，并更新到课程结构中
     *
     * @param item 课程结构项，包含课程信息和子项
     */
    private static void processCourseCatalogProgress(ApiCustomerUserCourseStructureRes item){
        List<ApiCustomerUserCourseStructureRes> children = item.getChildren();
        if (CollectionUtil.isEmpty(children)) {
            return;
        }
        // 计算用户在当前目录下观看过的视频课程数和总视频课程数
        List<ApiCustomerUserCourseStructureRes> lessonQuestionBankCourseList = children.stream()
                .filter(e -> CourseAssocResourceTypeEnum.VIDEO.getCode().equals(e.getType())).collect(Collectors.toList());
        Pair<Long, Integer> userCourseLessonPair = lessonQuestionBankCourseList.stream()
                .collect(Collectors.collectingAndThen(
                Collectors.toList(),
                list -> {
                    // 看完课程数
                    long listenedVideoCount = list.stream()
                            .filter(e -> Constants.VIDEO_END_FLAG.equals(e.getVideoListenStatus())).count();
                    // 该目录下总课程数
                    int catalogVideoCount = list.size();
                    return Pair.of(listenedVideoCount, catalogVideoCount);
                }
        ));
        // 用户练习题统计
        Pair<Long, Long> userCourseExercisePair = lessonQuestionBankCourseList.stream().collect(Collectors.collectingAndThen(
                Collectors.toList(),
                list -> {
                    // 看完课程数
                    long doExerciseCount = list.stream().filter(e -> SysConst.Flag.IS.getFlag().equals(e.getQuestionBankStudyStatus())).count();
                    // 该目录下总课程数
                    long exerciseCount = list.stream().filter(e -> ObjectUtil.isNotNull(e.getQuestionId())).count();
                    return Pair.of(doExerciseCount, exerciseCount);
                }
        ));
        if (userCourseLessonPair.getRight() > 0) {
            item.setListenProgress(StrUtil.format("{}/{}", userCourseLessonPair.getLeft(), userCourseLessonPair.getRight()));
        }
        if (userCourseExercisePair.getRight() > 0) {
            item.setExerciseProgress(StrUtil.format("{}/{}", userCourseExercisePair.getLeft(), userCourseExercisePair.getRight()));
        }
    }

    /**
     * 处理课程目录的进度
     * 该方法用于计算用户在课程结构中的学习进度，并更新到课程结构中
     *
     * @param item 课程结构项，包含课程信息和子项
     */
    private static void processCourseCatalogProgressNew(ApiCustomerUserCourseStructureRes item
            , ApiUserCourseStudyRecordRes userCourseStudyRecord){
        List<ApiCustomerUserCourseStructureRes> children = item.getChildren();
        if (CollectionUtil.isEmpty(children)) {
            return;
        }
        Map<Long, UserListen> userListenMap = ObjectUtil.defaultIfNull(userCourseStudyRecord.getUserListenMap(), Maps.newHashMap());
        Map<Long, UserExercise> userExerciseMap = ObjectUtil.defaultIfNull(userCourseStudyRecord.getUserExerciseMap(), Maps.newHashMap());
        // 计算用户在当前目录下观看过的视频课程数和总视频课程数
        List<ApiCustomerUserCourseStructureRes> lessonCourseList = children.stream()
                .filter(e -> CourseAssocResourceTypeEnum.VIDEO.getCode().equals(e.getType()))
                .collect(Collectors.toList());
        Pair<Long, Integer> userCourseLessonPair = lessonCourseList.stream().collect(Collectors.collectingAndThen(
                Collectors.toList(),
                list -> {
                    // 看完课程数
                    long listenedVideoCount = videoUserListened(list, userListenMap);
                    // 该目录下总课程数
                    int catalogVideoCount = list.size();
                    return Pair.of(listenedVideoCount, catalogVideoCount);
                }
        ));
        // 用户练习题统计
        Pair<Long, Long> userCourseExercisePair = lessonCourseList.stream().collect(Collectors.collectingAndThen(
                Collectors.toList(),
                list -> {
                    // 看完课程数
                    long doExerciseCount = exerciseUserDone(list, userExerciseMap);
                    // 该目录下总练习数
                    long exerciseCount = list.stream().filter(e -> ObjectUtil.isNotNull(e.getQuestionId())).count();
                    return Pair.of(doExerciseCount, exerciseCount);
                }
        ));
        if (userCourseLessonPair.getRight() > 0) {
            item.setListenProgress(StrUtil.format("{}/{}", userCourseLessonPair.getLeft()
                    , userCourseLessonPair.getRight()));
        }
        if (userCourseExercisePair.getRight() > 0) {
            item.setExerciseProgress(StrUtil.format("{}/{}", userCourseExercisePair.getLeft()
                    , userCourseExercisePair.getRight()));
        }
    }


    /**
     * 根据资源类型获取资源列表
     *
     * @param assocResourceStatOptional 可能包含课程资源信息的Optional对象
     * @param resourceTypeEnum 资源类型枚举
     * @return 返回一个Map，键为资源ID，值为资源对象
     *
     * 本方法首先尝试从给定的Optional<CourseResourceInfo>对象中获取与资源类型匹配的课程关联资源统计信息
     * 如果找到匹配的资源统计信息，则进一步获取资源ID列表，并通过特定的资源服务查询详细的资源信息
     * 最后，将查询到的资源信息以Map形式返回，便于快速访问
     */
    private Map<Long, ? extends ResourceCourseBase> getResourceListByType(Optional<CourseResourceInfo> assocResourceStatOptional, CourseAssocResourceTypeEnum resourceTypeEnum){
        Optional<CourseAssocResourceStat> courseAssocResourceOfflineStat = assocResourceStatOptional
                .map(CourseResourceInfo::getCourseAssocResourceList)
                .orElse(Lists.newArrayList()).stream()
                .filter(e -> resourceTypeEnum.getCode().equals(e.getResourceType())).findFirst();
        Map<Long, ? extends ResourceCourseBase> courseResourceMap = Maps.newHashMap();
        if (courseAssocResourceOfflineStat.isPresent()) {
            List<Long> resourceIdList = courseAssocResourceOfflineStat.get().getResourceIdList();
            ResourceCourseBaseExtendService resourceExtendService = CourseResourceFactory.getResourceServiceByType(resourceTypeEnum.getCode());
            List<? extends ResourceCourseBase> resourceCourseBaseList = resourceExtendService.listByIdList(resourceIdList);
            if (CollectionUtil.isNotEmpty(resourceCourseBaseList)) {
                courseResourceMap = resourceCourseBaseList.stream().collect(Collectors.toMap(ResourceCourseBase::getId, Function.identity()));
            }
        }
        return courseResourceMap;
    }

    private static long videoUserListened(List<ApiCustomerUserCourseStructureRes> lessonQuestionBankCourseList
            , Map<Long, UserListen> userListenMap){
        AtomicLong listenedVideoCount = new AtomicLong();
        lessonQuestionBankCourseList.forEach(e -> {
            UserListen userListen = userListenMap.get(e.getTypeAssoId());
            if (ObjectUtil.isNotNull(userListen) && Constants.VIDEO_END_FLAG.equals(userListen.getListeningStatus())) {
                listenedVideoCount.getAndIncrement();
            }
        });
        return listenedVideoCount.get();
    }

    private static long exerciseUserDone(List<ApiCustomerUserCourseStructureRes> lessonQuestionBankCourseList
            , Map<Long, UserExercise> userExerciseMap){
        AtomicLong exerciseCount = new AtomicLong();
        lessonQuestionBankCourseList.forEach(e -> {
            UserExercise userExercise = userExerciseMap.get(e.getQuestionId());
            if (ObjectUtil.isNotNull(userExercise)
                    && SysConst.Flag.IS.getFlag().equals(userExercise.getExerciseStatus())) {
                exerciseCount.getAndIncrement();
            }
        });
        return exerciseCount.get();
    }
}
